;(function(root, factory) {
    if (typeof exports === "object") {
        // CommonJS
        module.exports = exports = factory(require("crypto-js"));
    } else if (typeof define === "function" && define.amd) {
        // AMD
        define("SkyNetAuth", ["crypto-js"], factory);
    } else {
        // Global (browser)
        root.SkyNetAuth = factory(root.CryptoJS);
    }
}(this, function(CryptoJS) {
    var AUTH_KEY = "Authorization";

    var DIGEST_NAME = "SHA-256";

    function assemble(httpMethod, requestUrl, apiKey, apiSecret, body, apiKeyKey) {
        if (!httpMethod) {
            throw ("httpMethod is empty.");
        }
        if (!requestUrl) {
            throw ("requestUrl is empty.");
        }
        if (!apiKey) {
            throw ("apiKey is empty.");
        }
        if (!apiSecret) {
            throw ("apiSecret is empty.");
        }

        var httpRequestUrl = requestUrl.replace("ws://", "http://").replace("wss://", "https://");
        var url = new URL(httpRequestUrl);
        var dateStr = new Date().toUTCString();
        var sha = getSignature(url.hostname, dateStr, getRequestLine(httpMethod, url.pathname), apiSecret);

        var digest = null;
        if (body) {
            digest = signBody(body);

        }
        apiKeyKey = !apiKeyKey ? "hmac api_key" : apiKeyKey;
        var authorization = apiKeyKey + "=\"" + apiKey + "\", algorithm=\"hmac-sha256\", headers=\"host date request-line" + (!digest ? "" : " digest") + "\", signature=\"" + sha + "\"";
        var authorizationData = {};
        authorizationData.date = dateStr;
        authorizationData.host = url.hostname;
        authorizationData.authorization = authorization;
        authorizationData.digest = digest;
        return authorizationData;
    }

    function signBody(body) {

        if (!body) {
            throw ("body is empty.");
        }
        var sha256 = CryptoJS.SHA256(body);
        return CryptoJS.enc.Base64.stringify(sha256);

    }

    function getRequestLine(method, path) {
        return method.toUpperCase() + " " + path + " HTTP/1.1";
    }

    function getSignature(host, date, requestLine, apiSecret) {
        if (!host) {
            throw ("host is empty.");
        }
        if (!date) {
            throw ("date is empty.");
        }
        if (!requestLine) {
            throw ("requestLine is empty.");
        }
        if (!apiSecret) {
            throw ("apiSecret is empty.");
        }

        var str = "host: " + host + "\n" + "date: " + date + "\n" + requestLine;
        var hmacHasher = CryptoJS.HmacSHA256(str, apiSecret);
        return CryptoJS.enc.Base64.stringify(hmacHasher);
    }

    var SkyNetAuth = SkyNetAuth || {
        generateAuthUrl: function(apiKey, apiSecret, url, body, apiKeyKey) {
            if (!apiKey) {
                throw ("apiKey is empty.");
            }
            if (!apiSecret) {
                throw ("apiSecret is empty.");
            }
            if (!url) {
                throw ("url is empty.");
            }

            var authorizationData = assemble("GET", url, apiKey, apiSecret, body, apiKeyKey);
            var authBase = window.btoa(authorizationData.authorization);
            authBase = url + "?" + AUTH_KEY.toLowerCase() + "=" + encodeURIComponent(authBase) + "&host=" + encodeURIComponent(authorizationData.host) + "&date=" + encodeURIComponent(authorizationData.date);
            return authBase;
        }

    };
    return SkyNetAuth;
}));
